# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import typing

from coin.exchange.okex_v3.kr_rest.native_private_client import (
    OkexSyncQueryer,
    OkexAsyncQueryer,
    OkexOrderStatus,
    OkexOrderType,
    OkexFuturesOrderStatus,
    OkexFuturesOrderSide,
    OkexOrderProperty,
)


class OkexSwapNativePrivateClient(object):
  def __init__(
      self,
      *,
      key_file,
      api_host=None,
      disable_ssl=False,
      timeout=10,
      max_connections=8,
      use_async=False,
      rest_lib='pycurl',
  ):
    rest_lib = rest_lib or None
    if use_async:
      self._auth_queryer = OkexAsyncQueryer(key_file,
                                            api_host=api_host,
                                            disable_ssl=disable_ssl,
                                            timeout=timeout,
                                            max_connections=max_connections,
                                            rest_lib=rest_lib)
    else:
      self._auth_queryer = OkexSyncQueryer(key_file, api_host=api_host, timeout=timeout)

  def _query(self, method, path, params=None):
    if params is not None:
      params = {key: value for key, value in params.items() if value is not None}
    params = params or None
    return self._auth_queryer.query(method, path, params=params)

  def query_contract_positions(self, instrument_id):
    method = 'GET'
    path = '/api/swap/v3/{instrument_id}/position'
    path = path.format(instrument_id=instrument_id)
    update = self._query(method, path)
    return update

  def query_accounts(self):
    method = 'GET'
    path = '/api/swap/v3/accounts'
    update = self._query(method, path)
    return update

  def query_positions(self):
    method = 'GET'
    path = '/api/swap/v3/position'
    update = self._query(method, path)
    return update

  def query_single_account(self, *, instrument_id: str):
    method = 'GET'
    path = '/api/swap/v3/{instrument_id}/accounts'
    path = path.format(instrument_id=instrument_id)
    update = self._query(method, path)
    return update

  def query_user_setting(self, instrument_id):
    method = 'GET'
    path = '/api/swap/v3/{instrument_id}/accounts'
    path = path.format(instrument_id=instrument_id)
    update = self._query(method, path)
    return update

  def set_leverage(self, instrument_id, leverage, side):  # not tested
    # 'side: 1.LONG 2.SHORT 3.CROSS'
    method = 'POST'
    path = '/api/swap/v3/accounts/{instrument_id}/leverage'
    path = path.format(instrument_id=instrument_id)
    assert 1 <= leverage <= 40, leverage
    assert side in (1, 2, 3)
    params = {
        'leverage': leverage,
        'side': side,
    }
    update = self._query(method, path, params)
    return update

  def query_ledger(
      self,
      *,
      instrument_id=None,
      from_num=None,
      to_num=None,
      limit=None,
  ):
    method = 'GET'
    path = '/api/swap/v3/accounts/{instrument_id}/ledger'
    path = path.format(instrument_id=instrument_id)
    if limit is not None:
      assert 1 <= limit <= 100, limit
    params = {
        'from': from_num,
        'to': to_num,
        'limit': limit,
    }
    update = self._query(method, path, params)
    return update

  # Submit and cancel
  def place_order(
      self,
      *,
      order_type: OkexOrderType,
      order_side,
      instrument_id,
      price,
      size,
      client_oid=None,
      order_property: typing.Union[None, OkexOrderProperty] = None,
  ):
    method = 'POST'
    path = '/api/swap/v3/order'
    assert isinstance(order_side, OkexFuturesOrderSide), order_side
    if client_oid is not None:
      assert isinstance(client_oid, str), client_oid
    match_price = 0 if order_type == OkexOrderType.LIMIT else 1
    assert match_price == 0, 'Only limit order is allowed!'
    if order_property is not None:
      order_property = order_property.value
    params = {
        'client_oid': client_oid,
        'size': size,
        'type': order_side.value,
        'match_price': match_price,
        'price': price,
        'instrument_id': instrument_id,
        'order_type': order_property,  # Different terminology
    }
    update = self._query(method, path, params)
    return update

  def cancel_order(self, order_id, instrument_id):
    method = 'POST'
    path = '/api/swap/v3/cancel_order/{instrument_id}/{order_id}'
    path = path.format(instrument_id=instrument_id, order_id=order_id)
    update = self._query(method, path)
    return update

  def batch_place_orders(self, params):
    method = 'POST'
    path = '/api/swap/v3/batch_orders'
    update = self._query(method, path, params)
    return update

  def batch_cancel_orders(
      self,
      instrument_id,
      order_id_list,
  ):
    method = 'POST'
    path = '/api/swap/v3/cancel_batch_orders/{instrument_id}'
    path = path.format(instrument_id=instrument_id)
    params = {
        'order_ids': order_id_list,
    }
    update = self._query(method, path, params)
    return update

  def query_order_list(
      self,
      *,
      status: typing.Union[OkexOrderStatus, OkexFuturesOrderStatus],
      instrument_id: str,
      from_num=None,
      to_num=None,
      limit=None,
  ):
    method = 'GET'
    path = '/api/swap/v3/orders/{instrument_id}'
    if limit is not None:
      assert 1 <= limit <= 100, limit

    path = path.format(instrument_id=instrument_id)
    params = {
        'status': status.value,
        'from': from_num,
        'to': to_num,
        'limit': limit,
    }
    update = self._query(method, path, params)
    return update

  def query_order_detail(self, order_id, instrument_id):
    method = 'GET'
    path = '/api/swap/v3/orders/{instrument_id}/{order_id}'
    path = path.format(order_id=order_id, instrument_id=instrument_id)
    update = self._query(method, path)
    return update

  def query_transaction_details(
      self,
      order_id,
      instrument_id,
      from_num=None,
      to_num=None,
      limit=None,
  ):
    method = 'GET'
    path = '/api/swap/v3/fills'
    params = {
        'order_id': order_id,
        'instrument_id': instrument_id,
        'from': from_num,
        'to': to_num,
        'limit': limit,
    }
    update = self._query(method, path, params)
    return update
